import logging
from datetime import datetime


logger = logging.getLogger(__name__)


class SingleVarietyBacktestingModel:
    """单品种回测模型：负责参数校验与转换。

    - 注释使用中文
    - 日志（如有）使用英文
    """

    def build_vt_symbol(self, local_code: str, contract_type_text: str) -> str:
        """从本地代码与合约类型文本生成 vt_symbol。

        local_code 形如 "symbol.exchange"，如 "rb.SHFE"
        contract_type_text 为中文展示文本，如 "主力连续(888)" 或 "指数连续(99)"
        """
        if not local_code or '.' not in local_code:
            raise ValueError("本地代码格式不正确，应为 'symbol.exchange' 格式")

        symbol, exchange = local_code.split('.')
        suffix = '888' if contract_type_text == '主力连续(888)' else '99'
        return f"{symbol}{suffix}.{exchange}"

    def parse_date(self, date_str: str) -> datetime:
        """解析 YYYY/MM/DD 日期字符串。"""
        try:
            return datetime.strptime(date_str, "%Y/%m/%d")
        except Exception as e:
            logger.exception(e)
            raise ValueError(f"日期格式不正确，应为 YYYY/MM/DD 格式: {e}")

    def validate_positive_float(self, value_str: str, field_name_cn: str) -> float:
        """校验正浮点数。"""
        try:
            value = float(value_str)
        except Exception as e:
            logger.exception(e)
            raise ValueError(f"{field_name_cn}必须为数字")
        if value < 0:
            raise ValueError(f"{field_name_cn}不能为负数")
        return value

    def validate_positive_int(self, value_str: str, field_name_cn: str, allow_zero: bool = False) -> int:
        """校验正整数。"""
        try:
            # 允许传入浮点形式但需转为整数
            value = int(float(value_str))
        except Exception as e:
            logger.exception(e)
            raise ValueError(f"{field_name_cn}必须为整数")
        if allow_zero:
            if value < 0:
                raise ValueError(f"{field_name_cn}必须为非负整数")
        else:
            if value <= 0:
                raise ValueError(f"{field_name_cn}必须为正整数")
        return value

    def build_params(
        self,
        strategy_name: str,
        strategy_file_path: str,
        local_code: str,
        contract_type_text: str,
        kline_period: str,
        start_date_str: str,
        end_date_str: str,
        fee_rate_str: str,
        slippage_str: str,
        multiplier_str: str,
        price_tick_str: str,
        capital_str: str,
    ) -> dict:
        """从原始字符串参数构建并返回回测参数字典。

        若校验失败则抛出 ValueError（供视图层捕获并弹窗）。
        """
        # 必填校验
        raw_params = {
            "交易策略": strategy_name,
            "策略文件": strategy_file_path,
            "本地代码": local_code,
            "合约类型": contract_type_text,
            "K线周期": kline_period,
            "开始日期": start_date_str,
            "结束日期": end_date_str,
            "手续费率": fee_rate_str,
            "交易滑点": slippage_str,
            "合约乘数": multiplier_str,
            "价格跳动": price_tick_str,
            "回测资金": capital_str,
        }
        for field_name, value in raw_params.items():
            if value in (None, ""):
                raise ValueError(f"请填写参数: {field_name}")

        # 构建 vt_symbol
        vt_symbol = self.build_vt_symbol(local_code, contract_type_text)

        # 日期解析
        start_date = self.parse_date(start_date_str)
        end_date = self.parse_date(end_date_str)
        if start_date >= end_date:
            raise ValueError("开始日期必须早于结束日期")

        # 数值校验
        rate = self.validate_positive_float(fee_rate_str, "手续费率")
        slippage = self.validate_positive_float(slippage_str, "交易滑点")
        size = self.validate_positive_int(multiplier_str, "合约乘数")
        try:
            pricetick = float(price_tick_str)
        except Exception as e:
            logger.exception(e)
            raise ValueError("价格跳动必须为数字")
        if pricetick <= 0:
            raise ValueError("价格跳动必须为正数")
        capital = self.validate_positive_int(capital_str, "回测资金")

        return {
            "strategy_name": strategy_name,
            "strategy_file_path": strategy_file_path,
            "vt_symbol": vt_symbol,
            "interval": kline_period,
            "start_date": start_date,
            "end_date": end_date,
            "rate": rate,
            "slippage": slippage,
            "size": size,
            "pricetick": pricetick,
            "capital": capital,
        }


